package bancosys.tec.services;

import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

import jmine.tec.services.api.ServicesPage;
import jmine.tec.services.api.execution.ServicesPageExecutor;
import jmine.tec.services.integration.GlobalParameterService;
import jmine.tec.services.io.ServiceBean;
import jmine.tec.services.io.ServicesPageBean;
import jmine.tec.services.runner.BaseServiceBuilder;

import org.apache.commons.lang.StringUtils;

import bancosys.tec.services.input.driver.bean.ServicePageHeader;
import bancosys.tec.services.input.driver.processor.ThreadSafeServiceProcessor;

/**
 * Classe abstrato para testes de integração que se baseiam na execução de serviços a partir de arquivos.
 * 
 * @author Rafael Volpato (Jun 18, 2007)
 * @author lundberg (refactoring 2009-09-02)
 */
public abstract class ServicesIntegrationTestCase extends IntegrationTestCase {

    public static final String INTEGRATION_TEST_LOCATION_URL = "$SERVICESINTEGRATIONTESTCASE.LOCATION";

    /**
     * Construtor utilizado para criar uma instancia "vazia" do teste de integracao e obter quais sao os arquivos de test que devem ser
     * executados.
     */
    public ServicesIntegrationTestCase() {
        super();
    }

    /**
     * Construtor.
     * 
     * @param name nome do teste.
     */
    public ServicesIntegrationTestCase(String name) {
        super(name);
    }

    /**
     * Construtor utilizado para obter uma instancia de um teste de integracao que vai executar o arquivo de teste definido.
     * 
     * @param url arquivo de teste que vai ser executado
     */
    public ServicesIntegrationTestCase(URL url) {
        super(url);
    }

    /**
     * Executa os testes usando como referencia os testes descritos no datasource.
     * 
     * @exception Throwable if any exception is thrown
     */
    @Override
    protected void doRunTest() throws Throwable {
        List<ServicesPage> servicesPages = this.getServicesPages(this.getTestInput());
        assertNotNull("Ocorreu algum erro durante a leitura do arquivo.", servicesPages);
        this.validateServicesPages(servicesPages);
        this.executeServicesPages(servicesPages);
    }

    /**
     * Executa as páginas de serviços
     * 
     * @param servicesPages servicesPages
     * @exception Throwable if any exception is thrown
     */
    protected void executeServicesPages(List<ServicesPage> servicesPages) throws Throwable {
        this.getServicesPageExecutor().execute(servicesPages);
    }

    /**
     * Encontra os serviços a serem executados
     * 
     * @param url url
     * @return List de ServicesPage
     * @throws Throwable Throwable
     */
    protected List<ServicesPage> getServicesPages(URL url) throws Throwable {
        return this.getServicesFromURL(url);
    }

    /**
     * Executa as validacoes necessarias antes de executar o teste.
     * 
     * @return lista de erros encontrados.
     */
    private void validateServicesPages(List<ServicesPage> servicesPages) {
        try {
            List<String> errors = this.getServicesController().getServicesPageValidator().validate(servicesPages);
            if (errors.isEmpty()) {
                return;
            }
            for (String error : errors) {
                getLog().error(error);
            }
            fail("Existem " + errors.size() + " erros de validação no arquivo.");
        } catch (Throwable t) {
            fail("Ocorreu um erro ao tentar validar o teste: " + t.getMessage());
        }
    }

    /**
     * Retorna o DefaultServicesPageExecutor.
     * 
     * @return DefaultServicesPageExecutor
     */
    protected ServicesPageExecutor getServicesPageExecutor() {
        return this.getServicesController().getServicesPageExecutor();
    }

    /**
     * Lê os serviços do arquivo, se ele for um XLS ou XML de serviços
     * 
     * @param url url
     * @return List de ServicesPage
     * @throws Throwable Throwable
     */
    protected final List<ServicesPage> getServicesFromURL(final URL url) throws Throwable {
        final ThreadSafeServiceProcessor processor = new ThreadSafeServiceProcessor(this.getServicesController());
        final String fileCode = this.getFileCode(url);

        // Executa a leitura do arquivo de serviços dentro de uma nova transação
        ServicePageHeader header = (ServicePageHeader) this.digestFile(fileCode, url);
        this.addIntegrationTestGlobalParameters(url, header);
        return processor.getServicesPages(header);
    }

    protected void addIntegrationTestGlobalParameters(final URL url, ServicePageHeader header) {
        if (this.supportsGlobalParameters()) {
            ServicesPageBean page = new ServicesPageBean();
            page.addService(this.createGlobalParameter(INTEGRATION_TEST_LOCATION_URL, this.integrationTestLocation(url)));
            header.getWorkSheets().add(0, page);
        }
    }

    protected String integrationTestLocation(final URL url) {
        List<String> path = Arrays.asList(url.toExternalForm().split("/"));
        return StringUtils.join(path.subList(0, path.size() - 1), File.separator) + File.separator;
    }

    protected boolean supportsGlobalParameters() {
        return this.getServicesController().getServiceFactory().getServices().contains("PRE_REQ|GlobalParameter");
    }

    private ServiceBean createGlobalParameter(String parameter, String value) {
        BaseServiceBuilder builder = new BaseServiceBuilder(GlobalParameterService.class);
        builder.with(GlobalParameterService.REFERENCE, parameter);
        builder.with(GlobalParameterService.VALUE, value);
        return builder.build();
    }

}
